<!doctype html>
<meta charset="utf-8">
<html>
<head>
<!--- Allow injected scripts to use functions in fledge-util.sub.js --->
<base href="..">
<script src="/resources/testharness.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/common/utils.js"></script>
<script src="resources/fledge-util.sub.js"></script>
</head>
<body>
<script>

  // This can be used for either iframes or top-level windows.
  // If there is an opener, this is a top-level window, so
  // send messages to the opener. Otherwise, this is an iframe,
  // so send messages to the parent.
  let message_dest = window.opener;
  if (!message_dest)
    message_dest = window.parent;

  // Fake Test class that only supports adding cleanup callbacks,
  // primarily to leave interest groups once the test is complete.
  function Test() {
    this.cleanup_callbacks = [];
  }

  // Registers a cleanup method with Test.
  Test.prototype.add_cleanup = function(callback) {
    this.cleanup_callbacks.push(callback);
  };

  // Runs all previously registered cleanup methods, waiting for
  // them all to complete.
  Test.prototype.do_cleanup = async function() {
    while (this.cleanup_callbacks.length > 0) {
      await this.cleanup_callbacks[0]();
      this.cleanup_callbacks = this.cleanup_callbacks.slice(1);
    }
  };

  // Create a bogus test instance that tracks cleanup callbacks. The
  // main frame managing the test is expected to post a message
  // to run test_instance.do_cleanup() and wait for it to complete
  // before destroying the frame.
  let test_instance = new Test();

  // Register a message event listener that listens for events with data
  // in the format {messageUuid: <uuid>, script: <script>}, and when such
  // a message is received, tries to eval the script and then returns a
  // message in the format:
  //   {messageUuid: <uuid>, result: <result>, returnValue: <returnValue>}
  //
  // On success, <result> is "success", while on failure, it's an error
  // message. <script> is interpreted as a possibly asynchronous function
  // body. Exceptions are caught and their stringified value is returned
  // as <result>. <returnValue> is a value returned to the caller of
  // the function that sent the message. It's up to the received script
  // to set it, if a return value is needed.
  //
  // "messageUuid" serves to allow the listener to make sure the message
  // is intended for it.
  window.addEventListener('message', async function(event) {
    // If not a message for this listener, do nothing.
    if (!event.data.messageUuid)
      return;
    let message = {result: 'unexpected'};
    try {
      let param = event.data.param;
      message = await eval(
          `(async () => {
            ${event.data.script};
            return {result: 'success'};
          })()`);
    } catch (e) {
      message.result = e.toString();
    }
    message.messageUuid = event.data.messageUuid;

    message_dest.postMessage(message, '*');
  });

  // Inform "message_dest" that the frame has finished loading.
  message_dest.postMessage(
      {messageUuid: '{{GET[uuid]}}', result: 'load complete'},
      '*');
</script>
</body>
</html>
